package com.zeny.express.framework.log.aop;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;

import com.zeny.express.framework.log.annotation.LogBusiness;
import com.zeny.express.framework.log.entity.BusinessLog;
import com.zeny.express.framework.log.log4j.AsyncLoggerFactory;
import com.zeny.express.framework.shared.domain.Constants;

/**
 * Log Service Interceptor
 * 
 * <pre>
 * use spring aop(interface-based proxy) such as [Transactional, Cacheable]
 * instead of spring aspectj aop(class-based proxy)
 * because of it could be annotated on the java interface
 * </pre>
 * 
 * @author zhuwei
 * @date 2013年10月12日 上午8:51:37
 */
public class BusinessInterceptor extends BusinessAspectSupport implements MethodInterceptor {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(BusinessInterceptor.class);

	private AsyncLoggerFactory asyncLoggerFactory;

	public BusinessInterceptor() {
		LOGGER.info("[framework] construct LogServiceInterceptor");
	}

	/**
	 * @param asyncLoggerFactory
	 */
	public void setAsyncLoggerFactory(AsyncLoggerFactory asyncLoggerFactory) {
		this.asyncLoggerFactory = asyncLoggerFactory;
	}

	public Object invoke(final MethodInvocation invocation) throws Throwable {

		LOGGER.trace("[framework] begin invoke:{}", invocation.getMethod().getName());

		Method method = invocation.getMethod();

		BusinessLog businessLog = new BusinessLog();

		// get backing class
		Object target = invocation.getThis();
		Object[] args = invocation.getArguments();
		
		Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
		if (targetClass == null && target != null) {
			targetClass = target.getClass();
		}
		
		// get annotation info
		LogBusiness logBusiness = method.getAnnotation(LogBusiness.class);
		
		BusinessOperation operation = new BusinessOperation();
		operation.setEntityId(logBusiness.entityId());
		operation.setEntityCode(logBusiness.entityCode());
		operation.setEntityName(logBusiness.entityName());
		
		Object result = BusinessExpressionEvaluator.NO_RESULT;
		
		try {
			
			result = invocation.proceed();
			businessLog.setIsException(Constants.NO);
			return result;
			
		} catch (Throwable t) {
			
			businessLog.setIsException(Constants.YES);
			businessLog.setExceptionMessage(t.toString());
			throw t;
			
		} finally {

			// calc spring expression 
			OperationContext operationContext = new OperationContext(operation, method, args, target, targetClass);
			Object entityId = operationContext.generateEntityId(result);
			Object entityCode = operationContext.generateEntityCode(result);
			Object entityName = operationContext.generateEntityName(result);
			
			constructBusinessLog(businessLog, logBusiness, entityId, entityCode, entityName);

			asyncLoggerFactory.asyncLogDB(businessLog);
			
			LOGGER.trace("[framework] end invoke:{}", invocation.getMethod().getName());
		}
	}
	
}
